package ocbn.remotelab.xml.helpers;

import java.util.Vector;
import net.rim.device.api.xml.jaxp.DOMInternalRepresentation;
import net.rim.device.api.xml.parsers.DocumentBuilderFactory;
import net.rim.device.api.xml.parsers.ParserConfigurationException;
import ocbn.remotelab.exceptions.XMLAttributesBuilderException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
 * Builds an Attributes object for use in XML export. Uses DOM to generate the
 * attributes and then SAX to parse the attributes to get an Attributes object.
 * This is necessary because RIM's SAX library doesn't provide a way to
 * construct an Attributes object.
 *
 * @author Ke Li
 */
public class AttributesBuilder {

    /**
     * A 2D Vector of Strings representing the list of attributes
     */

    private Vector attributes;

    /**
     * An Attributes object representing empty attributes (no attributes)
     */

    private static Attributes emptyAttributes;

    /**
     * Constructs a new instance of AttributesBuilder that builds an Attributes
     * object for an XML element. 
     */

    public AttributesBuilder(){
        attributes = new Vector();
    }

    /**
     * Adds an attribute with the specified name.
     *
     * @param name      The name of the attribute
     * @param value     The value of the attribute
     *
     * @see org.w3c.dom.Element#setAttribute
     */

    public void addAttribute(String name, String value){
        Vector tempVector = new Vector();
        tempVector.addElement(name);
        tempVector.addElement(value);
        attributes.addElement(tempVector);
    }

    /**
     * Adds an attribute with the specified name and namespace. 
     *
     * @param namespaceURI  The namespace URI of the attribute
     * @param qualifiedName The qualified name of the attribute
     * @param value         The value of the attribute
     *
     * @see org.w3c.dom.Element#setAttributeNS
     */

    public void addAttributeNS(String namespaceURI, String qualifiedName,
            String value){
        Vector tempVector = new Vector();
        tempVector.addElement(namespaceURI);
        tempVector.addElement(qualifiedName);
        tempVector.addElement(value);
        attributes.addElement(tempVector);
    }

    /**
     * Gets an Attributes object that represents empty attributes (no
     * attributes). 
     *
     * @return      An Attributes object representing empty attributes
     */

    public static Attributes getEmptyAttributes() {
        try{
            if(emptyAttributes == null) {
                emptyAttributes = getElementAttributes(getNewTempElement());
            }
        } catch (SAXException e) {
            System.err.println("XML Parsing Error");
        } catch (ParserConfigurationException e) {
            System.err.println("XML Parser Configuration Error");
        } catch (XMLAttributesBuilderException e) {
            System.err.println("XML Attributes Builder Error");
            System.err.println(e.getMessage());
        }
        return emptyAttributes;
    }

    /**
     * Gets a new temporary Element object that can be used by DOM APIs.
     *
     * @return      An empty Element object with the name "temp"
     * @throws ParserConfigurationException     Indicates that the XML parser
     *                                          is incorrectly configured
     */

    private static Element getNewTempElement()
            throws ParserConfigurationException {
        Document tempDocument = DocumentBuilderFactory.newInstance().
                newDocumentBuilder().newDocument();
        Element tempElement = tempDocument.createElement("temp");
        tempDocument.appendChild(tempElement);
        return tempElement;
    }

    /**
     * Gets the attributes of an element as an Attributes object.
     * 
     * @param element           An Element object from which the attributes will
     *                          be extracted
     * @return                  An Attributes object representing the attributes
     *                          of the element
     * @throws SAXException     Indicates that an error occured in the XML SAX
     *                          parser
     * @throws XMLAttributesBuilderException    Indicates that an error occured
     *                                          in the XML Attributes Builder
     *                                          that most likely results from
     *                                          programming mistakes
     */

    private static Attributes getElementAttributes(Element element)
            throws SAXException, XMLAttributesBuilderException {
        Document document = element.getOwnerDocument();
        if (document == null) {
            throw new XMLAttributesBuilderException();
        }
        AttributesRetrievalHandler attributesHandler
                = new AttributesRetrievalHandler();
        DOMInternalRepresentation.parse(document, attributesHandler);
        return attributesHandler.getAttributes();
    }

    /**
     * Get an Attributes object representing the attributes supplied to this
     * class via addAttribute or addAttributeNS methods.
     *
     * @return      An Attributes object representing the attributes added via
     *              addAttribute or addAttributeNS methods
     */

    public Attributes getAttributes(){
        try {
            Element tempElement = getNewTempElement();
            Vector currentAttribute;
            for (int i = 0; i < attributes.size(); i++) {
                currentAttribute = (Vector) attributes.elementAt(i);
                if (currentAttribute.size() == 2) {
                    tempElement.setAttribute
                            ((String) currentAttribute.elementAt(0),
                            (String) currentAttribute.elementAt(1));
                } else if (currentAttribute.size() == 3) {
                    tempElement.setAttributeNS
                            ((String) currentAttribute.elementAt(0),
                            (String) currentAttribute.elementAt(1),
                            (String) currentAttribute.elementAt(2));
                }
            }
            return getElementAttributes(tempElement);
        } catch (SAXException e) {
            System.err.println("XML Parsing Error");
        } catch (ParserConfigurationException e) {
            System.err.println("XML Parser Configuration Error");
        } catch (XMLAttributesBuilderException e) {
            System.err.println("XML Attributes Builder Error");
            System.err.println(e.getMessage());
        }
        return getEmptyAttributes();
    }

    /**
     * A private inner class that retrives the attributes of the last element of
     * an XML document when parsed by an XML SAX parser. It extends the
     * DefaultHandler class in the SAX library.
     */

    private static class AttributesRetrievalHandler extends DefaultHandler {

        /**
         * The temporary attributes of the currently parsed element
         */

        Attributes tempAttributes;

        /**
         * Called when the parser encounters encounters the start of an
         * element.
         *
         * @param uri           A String representing the URI of the element
         * @param localName     A String representing the local name
         * @param qName         A String representing the qualified name
         * @param attributes    An Attributes object representing the attributes
         *                      of the element
         * @throws SAXException Indicates that an error occured when parsing the
         *                      XML
         */

        public void startElement(String uri, String localName, String qName,
                Attributes attributes) throws SAXException {
            tempAttributes = attributes;
        }

        /**
         * Gets the attributes of the last element of the XML document. 
         *
         * @return      An Attributes object representing the attributes of the
         *              last element
         */

        public Attributes getAttributes(){
            return tempAttributes;
        }

    }

}
